#pragma once
#include<iostream>
#include<string>
#include<memory>
#include<sys/epoll.h>
#include"InetAddr.hpp"
#include"Socket.hpp"
#include"Log.hpp"

using namespace socket_ns;

class EpollServer
{
    const static int gnum = 64;
    public:
    EpollServer(uint16_t port=8888)
        :_port(port),
        _listensock(std::make_unique<TcpSocket>()),
        _epfd(-1)
    {
        //创建listensocket
        InetAddr addr("0",_port);
        _listensock->BuildListenSocket(addr);
        //2:创建epoll模型
        _epfd = ::epoll_create(128);//用户管理多文件描述符的事件通知， size告诉内核监听的文件描述符数量
        //成功返回非负数的描述符，失败返回-1
        if(_epfd<0)
        {   
            LOG(FATAL,"epoll_create error\n");
            exit(5);
        }
        LOG(DEBUG,"epoll_create success,epfd:%d\n",_epfd);
        //只有一个listen scok 关心的事件：读事件
        struct epoll_event ev;
        //struct epoll_event {
        //uint32_t events; // 表示感兴趣的事件和被触发的事件
        //epoll_data_t data; // 用户数据，可以是指针、文件描述符等
        //};
        ev.events = EPOLLIN;//文件描述符可读；
        ev.data.fd  = _listensock->SockFD();//监听listsock文件描述符
        epoll_ctl(_epfd,EPOLL_CTL_ADD,_listensock->SockFD(),&ev);//多路复用IO接口
        //控制epoll文件描述符删给的事件，注册，修改，删除，的事件，
        //这个函数想epfd(文件描述符)引用的epoll实例执行控制操作，有 添加EPOLL_CTL_ADD,删除EPOLL_CTL_DEL，修改EPOLL_CTL_MOD

    }
    ~EpollServer()
    {
        _listensock->Close();
        if(_epfd>=0)
        {
            ::close(_epfd);
        }
    }
    void handlerEvent(int num)//处理事件
    {
        for(int i = 0;i<num;i++)
        {
            //获取想要知道的
            uint32_t reverts = _revs[i].events;//事件
            int sockfd = _revs[i].data.fd;//文件描述符
            //读事件就绪
            if(reverts & EPOLLIN)
            {
                if(sockfd==_listensock->SockFD())
                {
                InetAddr clientaddr;//客户端地址
                int newfd = _listensock->Accepter(&clientaddr);//新的文件描述符；
                if(newfd<0)
                    continue;
                //获取新连接成功
                //epoll知道newnfd上面有对应的事件就绪，
                struct epoll_event ev;
                ev.events = EPOLLIN;
                ev.data.fd = newfd;
                epoll_ctl(_epfd,EPOLL_CTL_ADD,newfd,&ev);
                LOG(DEBUG,"_listensock ready,accept done,epoll_ctl done,newfd,is %d\n",newfd);
                }
                else
                {
                    char buffer[1024];
                    ssize_t n = ::recv(sockfd,buffer,sizeof(buffer),0);//从缓冲区中读取size个字节
                    if(n>0)
                    {
                        LOG(DEBUG,"normal fd %d readt,recvbegin...\n",sockfd);
                        buffer[n] = 0;
                        std::cout <<"client say# "<<buffer<<std::endl;

                        std::string echo_string ="server echo# ";
                        echo_string+=buffer;
                        ::send(sockfd,echo_string.c_str(),echo_string.size(),0);
                    }
                    else if(n==0)
                    {
                        LOG(DEBUG,"normal fd %d close me too \n",sockfd);
                        //对方连接关闭
                        ::epoll_ctl(_epfd,EPOLL_CTL_DEL,sockfd,nullptr);//对方断开连接删除文件描述符，
                        ::close(sockfd);
                    }
                    else
                    {
                        ::epoll_ctl(_epfd,EPOLL_CTL_DEL,sockfd,nullptr);
                        ::close(sockfd);
                    }
                }
            }
        }
    }
    void Loop()
    {
        int timeout = -1;
        while(true)
        {
            int n = ::epoll_wait(_epfd,_revs,gnum,timeout);
            switch (n)
            {
            case 0:
                LOG(DEBUG,"epoll_wait timeout...\n");
                break;
            case -1:
                LOG(DEBUG,"epoll_wait failed...\n");
            break;
            default:
                LOG(DEBUG,"epoll_wait haved event ready...,n:%d\n",n);
                //handlerEvent(n);
                break;
            }
            
        }
    } 
    private:
    uint16_t _port;
    std::unique_ptr<Socket> _listensock;
    int _epfd;
    struct epoll_event _revs[gnum];
};